package sweet.helper

import java.io.{ File, InputStream, FileInputStream }
import java.{ util=> jutil }
import scala.collection.{ mutable, immutable, jcl }
import StringHelper._

/** A trait that provide init/destroy life cycle.
 *
 * When using as stackble chain of initialization for an objects
 * with multiple sub trait of this, the sub trait MUST <code>override</code> 
 * #init or #destroy, and MUST calls #super.init to chain ALL 
 * linear initialization.
 *
 * Example:
 * <pre>
 *   trait T1 extends InitDestroy { override def init{ super.init; println("T1 stuff goes here") }
 *   trait T2 extends InitDestroy { override def init{ super.init; println("T2 stuff goes here") }
 *   class MyInitializer extends Object with T1 with T2
 *   new MyInitializer().init //<-- will call both T1 and T2 init methods!
 * </pre>
 */
trait InitDestroy {
  def init{}
  def destroy{}  
}

/** A container for InitDestroy.
 *
 * This trait is provide alternative solution to collect all InitDestroy instances
 * then perform custom codes. 
 */
trait InitDestroyContainer {
  def initsDestroys = _initsDestroys.toList
  val _initsDestroys = new mutable.ListBuffer[InitDestroy]
  
  /** abstract control to add initializable container */
  def initsDestroys(xs: InitDestroy*){ _initsDestroys ++= xs }
}

/** A container using #addProps and #addSysProps as abstract controls 
 * to manipulate #props member. 
 * 
 * This container doesn't have a corresponding managed object, but rather
 * providing a #props as lookup table for sub-classes to uses. The addXXX
 * methods will populate #props accordingly.
 */
trait PropsContainer {
  def props = _props.readOnly
  val _props = new mutable.HashMap[String, String]
  
  /** abstract control to load properties from System.getProperties or System.getEnv. */
  def addProps(pairs: (String, String)*){
    _props ++= pairs
  }
  
  /** abstract control to load properties from file. */
  def addProps(file: File){
    addProps(new FileInputStream(file))
  }
  
  /** abstract control to load properties from file. */
  def addProps(ins: InputStream){
    val hash = new SProperties(JProperties(ins))
    _props ++= hash
  }
  
  /** abstract control to load properties from file. */
  def addProps(hash: scala.collection.Map[String,String]){
    _props ++= hash
  }
  
  /** abstract control to find a property from System.getProperties. If not found then
   * translate name from "." to "_" and tries to find it from System.getEnv. If still 
   * not found then return the default value. 
   * Note if ENV value is found, both the orig name and translated name will be inserted with 
   * it's value! */
  def addSysProps(name: String, default: String){
    JSystem.props.get(name) match {
      case Some(sv) => _props += Pair(name, sv) //first case
      case None => {
        val envName = name.toUpperCase.fastReplace(".", "_")
        JSystem.env.get(envName) match {
          case Some(ev) => { //second case
            _props += Pair(name, ev)
            _props += Pair(envName, ev)
          }
          case None => _props += Pair(name, default) //third case
        }
      }
    }    
  }
  
  /** this will let subclass to load a properties file as input stream by specifying
   * "classpath:/mypackage/myresource.txt" format. */
  def resourceAsStream(res: String): InputStream = {
    val clspath = "classpath:"
    val resName = if(res.startsWith(clspath)) res.substring(clspath.length) else res
    val stream = this.getClass.getClassLoader.getResourceAsStream(resName)
    if(stream == null) //give a more friendier exception msg instead of NPE.
      throw new Exception("Resource "+res+" can not be found")
    stream
  }
}

